/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.util;
import java.util.AbstractSet;
import java.util.Set;
import java.util.Iterator;
import java.util.Collection;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
import java.io.Serializable;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
/** Set which holds its members by using of WeakReferences.
* MT level: unsafe.
*
* @author Ales Novak
*/
public class WeakSet extends AbstractSet implements Cloneable, Serializable {
/** load factor */
private float loadFactor;
/** Number of items. */
private int size;
/** Modification count */
private long modcount;
/** Reference queue of collected weak refs */
private transient ReferenceQueue refq;
/** Count of <tt>null</tt> in this set */
long nullCount;
/** An array of Entries */
private transient Entry[] entries;
transient Entry iterChain;
static final long serialVersionUID =3062376055928236721L;
/** Constructs a new set. */
public WeakSet() {
this (101, 0.75f);
}
/** Constructs a new set containing the elements in the specified collection.
* @param c a collection to add
*/
public WeakSet(Collection c) {
this ();
addAll(c);
}
/** Constructs a new, empty set;
* @param initial capacity
*/
public WeakSet(int initialCapacity) {
this (initialCapacity, 0.75f);
}
/** Constructs a new, empty set;
*
* @param initialCapacity
* @param loadFactor.
*/
public WeakSet(int initialCapacity, float loadFactor) {
if (initialCapacity <= 0 || loadFactor <= 0) {
throw new IllegalArgumentException();
}
size = 0;
modcount = 0;
this.loadFactor = loadFactor;
nullCount = 0;
refq = new ReferenceQueue();
entries = new Entry[initialCapacity];
iterChain = null;
}
/** Adds the specified element to this set if it is not already present.
*
* @param o an Object to add
*/
public boolean add(Object o) {
if (o == null) {
size++;
nullCount++;
modcount++;
return true;
}
Entry e = object2Entry(o);
if (e != null) {
return false;
}
modcount++;
size++;
int hash = hashIt(o);
Entry next = entries[hash];
iterChain = entries[hash] = new Entry(o, refq, next, iterChain);
rehash();
return true;
}
/** Removes all of the elements from this set. */
public void clear() {
for (int i = 0; i < entries.length; i++) {
entries[i] = null;
}
nullCount = 0;
modcount++;
size = 0;
iterChain = null;
}
/** Returns a shallow copy of this WeakSet instance: the elements themselves are not cloned. */
public Object clone() {
WeakSet nws = new WeakSet(1, loadFactor);
nws.size = size;
nws.nullCount = nullCount;
Entry[] cloned = new Entry[entries.length];
nws.entries = cloned;
for (int i = 0; i < cloned.length; i++) {
cloned[i] = (entries[i] == null ? null : entries[i].clone(nws.refq));
// chains into nws iterator chain
Entry entry = cloned[i];
while (entry != null) {
entry.chainIntoIter(nws.iterChain);
nws.iterChain = entry;
entry = entry.next;
}
}
return nws;
}
/** Returns true if this set contains the specified element.
*
* @param o an Object to examine
*/
public boolean contains(Object o) {
if (o == null) {
return nullCount > 0;
}
return object2Entry(o) != null;
}
/** Returns true if this set contains no elements.
*/
public boolean isEmpty() {
return ((nullCount == 0) &&
(size() == 0));
}
/** Returns an iterator over the elements in this set. */
public Iterator iterator() {
return new WeakSetIterator();
}
class WeakSetIterator implements Iterator {
Entry current;
Entry next;
Object currentObj;
Object nextObj;
final long myModcount;
long myNullCount;
WeakSetIterator() {
myModcount = modCount();
myNullCount = nullCount;
current = null;
next = null;
Entry ee = iterChain;
if (ee == null) {
return;
}
Object o = ee.get();
while (ee.isEnqueued()) {
ee = ee.iterChainNext;
if (ee == null) {
return;
}
o = ee.get();
}
nextObj = o;
next = ee;
}
public boolean hasNext() {
checkModcount();
return (myNullCount > 0 || next != null);
}
public Object next() {
checkModcount();
checkRefQueue();
if (myNullCount > 0) {
myNullCount--;
return null;
} else {
if (next == null) {
throw new java.util.NoSuchElementException();
}
current = next;
currentObj = nextObj;
// move to next requested
do {
next = next.iterChainNext;
if (next == null) {
break;
}
nextObj = next.get();
} while (next.isEnqueued());
return currentObj;
}
}
public void remove() {
checkModcount();
if (current == null) {
throw new IllegalStateException();
}
current.remove();
}
void checkModcount() {
if (myModcount != modCount()) {
throw new java.util.ConcurrentModificationException();
}
}
}
/** Removes the given element from this set if it is present.
*
* @param o an Object to remove
* @return <tt>true</tt> if and only if the Object was successfuly removed.
*/
public boolean remove(Object o) {
if (o == null) {
if (nullCount > 0) {
nullCount--;
modcount++;
size--;
}
return true;
}
Entry e = object2Entry(o);
if (e != null) {
modcount++;
size--;
e.remove();
rehash();
return true;
}
return false;
}
/** @return the number of elements in this set (its cardinality). */
public int size() {
return size;
}
/** Checks if the queue is empty if not pending weak refs are removed. */
void checkRefQueue() {
for (;;) {
Entry entry = (Entry) refq.poll();
if (entry == null) {
break;
}
entry.remove();
}
}
/** @return modcount */
long modCount() {
return modcount;
}
/** @return an index to entries array */
int hashIt(Object o) {
return (o.hashCode() & 0x7fffffff) % entries.length;
}
/** rehashes this Set */
void rehash() {
/*
float currentLF = ((float) size) / ((float) entries.length);
if (currentLF < loadFactor) {
return;
}
*/
}
/** @return an Entry with given object */
private Entry object2Entry(Object o) {
checkRefQueue(); // clear ref q
int hash = hashIt(o);
Entry e = entries[hash];
if (e == null) {
return null;
}
while ((e != null) && !e.equals(o)) {
e = e.next;
}
return e;
}
private void writeObject(ObjectOutputStream obtos) throws IOException {
obtos.defaultWriteObject();
obtos.writeObject(toArray());
}
private void readObject(ObjectInputStream obtis) throws IOException,
ClassNotFoundException {
obtis.defaultReadObject();
Object[] arr = (Object[]) obtis.readObject();
entries = new Entry[(int) (size * 1.5)];
refq = new ReferenceQueue();
for (int i = 0; i < arr.length; i++) {
add(arr[i]);
}
}
/** Entries of this set */
class Entry extends WeakReference {
// double linked list
Entry prev;
Entry next;
private final int hashcode;
Entry iterChainNext;
Entry iterChainPrev;
Entry(Object referenced, ReferenceQueue q, Entry next, Entry nextInIter) {
super (referenced, q);
this.next = next;
this.prev = null;
if (next != null) {
next.prev = this;
}
hashcode = referenced.hashCode();
chainIntoIter(nextInIter);
}
void chainIntoIter(Entry nextInIter) {
iterChainNext = nextInIter;
if (nextInIter != null) {
nextInIter.iterChainPrev = this;
}
}
/** deques itself */
void remove() {
if (prev != null) {
prev.next = next;
}
if (next != null) {
next.prev = prev;
}
if (iterChainNext != null) {
iterChainNext.iterChainPrev = iterChainPrev;
}
if (iterChainPrev != null) {
iterChainPrev.iterChainNext = iterChainNext;
} else { // root
iterChain = iterChainNext;
}
}
public int hashCode() {
return hashcode;
}
public boolean equals(Object o) {
Object oo = get();
if (oo == null) {
return false;
} else {
return oo.equals(o);
}
}
public Entry clone(ReferenceQueue q) {
return new Entry(get(), q, (next != null ? (Entry) next.clone(q) : null), null);
}
}
}
/*
* Log
* 4 Gandalf 1.3 10/26/99 Ales Novak #4539
* 3 Gandalf 1.2 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 2 Gandalf 1.1 8/9/99 Ian Formanek Generated Serial Version
* UID
* 1 Gandalf 1.0 7/1/99 Ales Novak
* $
*/